package coreModel;

import hitExceptions.InvalidDataException;
import hitExceptions.InvalidOperationException;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import com.sun.tools.javac.sym.CreateSymbols;

/**
 * This class is to be used for storing an item with a unique barcode. 
 * It stores the entry date and expiration date.
 * @author The Fantastic 4
 *
 */
public class Item extends SerializableImplementer{



	private Product parentProduct;
	private Barcode itemBarcode;
	private Date entryDate;
	private Date expirationDate;
	private Date exitTime;
	private ProductContainer parentContainer;

	private final static int MONTH_MIN = 1;
	private final static int DOM_MIN = 1;
	private final static int YEAR_MIN = 2000;

	/**
	 * Constructor, constructs an empty Item
	 */
	public Item()
	{
		itemBarcode = new Barcode();
		parentProduct = null;
		parentContainer = null;
		entryDate = null;
		expirationDate = null;
	}

	/**
	 * Constructor, constructs an Item with the given information
	 * @param barcode the unit's unique barcode
	 * @param product the product object that is this item
	 * @param parentContainer the container in which this product is housed
	 * @throws InvalidDataException 
	 */
	public Item(Barcode bc, Product pp,ProductContainer pc) throws InvalidDataException
	{
		itemBarcode = bc;
		//parentProduct = pp;
		setProduct(pp); // add to check for null or nonexistant parent
		parentContainer = pc;
		entryDate = null;
		exitTime = null;
		expirationDate = null;
	}

	/**
	 * Copy Constructor, returns a shallow copy of an Item
	 * METHOD ADDED BY TYLER FOR EASE OF USE
	 */
	public Item(Item old) 
	{
		this.parentProduct = old.parentProduct;
		this.itemBarcode = old.itemBarcode;
		this.entryDate = old.entryDate;
		this.expirationDate = old.expirationDate;
		this.exitTime = old.exitTime;
		this.parentContainer = old.parentContainer;
	}

	/**
	 * return the product object that is associated with this item
	 * @return returns a Product object
	 */
	public Product getProduct() 
	{
		return parentProduct;
	}


	/**
	 * Sets the product object for this item
	 * @param product 
	 * @throws InvalidDataException 
	 */
	public void setProduct(Product product) throws InvalidDataException 
	{
		if(!HIT.getInstance().getProductManager().productExists(product) ||
				product==null)
			throw new InvalidDataException("Items must have a product in the System");
		this.parentProduct = product;
	}

	/**
	 * Return the barcode for this the item 
	 * @return barcode object
	 */
	public Barcode getBarcode()
	{
		return itemBarcode;
	}

	/**
	 * Sets the item barcode 
	 * @param barcode a valid Barcode object
	 */
	public void setBarcode(Barcode barcode) 
	{
		this.itemBarcode = barcode;
	}

	/**
	 * returns a Date object that is the date the object entered the system
	 * @return Date object with hours, minutes, and seconds excluded.
	 */
	public Date getEntryDate() 
	{
		return entryDate;
	}

	/**
	 * 
	 * @return a Date object that includes the date and time of exit of the item from the system.
	 */
	public Date getExitTime()
	{
		return exitTime;
	}

	/**
	 * @return the expiration date of the item if it has one. Otherwise it returns null.
	 */
	public Date getExpirationDate()
	{
		return expirationDate;
	}

	/**
	 * Sets and items entry date to a java.util.date object
	 * @param entDate the date the Item was added. Hours, minutes, and seconds will be cut off.
	 * @throws InvalidDataException when the date is earlier than 1 Jan 2000
	 */
	public void setEntryDate(Date entDate) throws InvalidDataException
	{
		GregorianCalendar newEntryDate = new GregorianCalendar();
		newEntryDate.setTime(entDate);
		newEntryDate.set(Calendar.HOUR_OF_DAY, 0);
		newEntryDate.set(Calendar.MINUTE, 0);
		newEntryDate.set(Calendar.SECOND, 0);
		newEntryDate.set(Calendar.MILLISECOND, 0);

		GregorianCalendar today = new GregorianCalendar();
		today.set(Calendar.HOUR_OF_DAY, 0);
		today.set(Calendar.MINUTE, 0);
		today.set(Calendar.SECOND, 0);
		today.set(Calendar.MILLISECOND, 0);

		GregorianCalendar earliestDay = new GregorianCalendar();
		earliestDay.clear();
		earliestDay.set(2000, 0, 1, 0, 0, 0);

		if (newEntryDate.compareTo(earliestDay) < 0)
			throw new InvalidDataException("That date is before 1 Jan 2000. You can't do that!");

		if (newEntryDate.compareTo(today) > 0)
			throw new InvalidDataException("That date is future. You can't do that!");

		entryDate = newEntryDate.getTime();
		
		
		// added to fix nonExistentProduct bug
		if (parentProduct!=null && parentProduct.getShelfLife() != 0)
		{
			GregorianCalendar newExpirationDate = new GregorianCalendar();
			newExpirationDate.setTime(entryDate);
			newExpirationDate.add(Calendar.MONTH, parentProduct.getShelfLife());
			expirationDate = newExpirationDate.getTime();
		}
		else
		{
			expirationDate = null;
		}

	}

	/**
	 * 
	 * @param exit a Date object with the date and time of the items exit from the inventory.
	 * @throws InvalidDataException when the exit time is earlier than the entry time and when
	 * the exit time is in the future
	 */
	public void setExitTime(Date exit) throws InvalidDataException
	{
		if (exit.compareTo(entryDate) < 0)
			throw new InvalidDataException("That exit time is earlier than the entry date, silly!");

		Date now = new Date();

		if (exit.compareTo(now) > 0)
			throw new InvalidDataException("That exit time is in the future. That's not allowed!");

		exitTime = exit;
	}

	/**
	 * Return a ProductContainer object were the item is stored
	 * @return the product container that holds the item
	 */
	public ProductContainer getParentContainer() 
	{
		return parentContainer;
	}

	/**
	 * Sets the parent container for the given Item
	 * @param parentContainer
	 */
	public void setParentContainer(ProductContainer pc) 
	{
		this.parentContainer = pc;
	}

	/**
	 * Checks the items expiration date with the current date and return true if the item is expired
	 * @return true if the expirationDate is latter then the current date
	 */
	public boolean isExpired()
	{
		GregorianCalendar expiredDate= new GregorianCalendar();
		expiredDate.setTime(entryDate);
		expiredDate.add(Calendar.MONTH, parentProduct.getShelfLife());
		expirationDate=expiredDate.getTime();
		
		GregorianCalendar today = new GregorianCalendar();
		today.set(Calendar.HOUR_OF_DAY, 0);
		today.set(Calendar.MINUTE, 0);
		today.set(Calendar.SECOND, 0);
		today.set(Calendar.MILLISECOND, 0);
		// added a null check
		if (expirationDate==null || expirationDate.compareTo(today.getTime()) > 0 )
			return false;
		else
			return true;
	}

	/**
	 * toString method
	 * @return a String with the barcode
	 */
	public String toString()
	{
		return itemBarcode.getValue();
	}


	public boolean equals(Object obj) 
	{
		Item i = (Item)obj;

		if ( itemBarcode.equals(i.getBarcode()) )
			return true;
		else
			return false;
	}


	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode()
	{
		final int prime = 31;
		int result = 1;
		result = prime * result
		+ ((itemBarcode == null) ? 0 : itemBarcode.hashCode());
		return result;
	}


	/*
	 * **********************************************************************************
	 * This section is for methods that test the functionality of the class
	 * **********************************************************************************
	 */




	public static boolean testIsExpired()
	{
		boolean result = true;

		try		
		{	
			Product p = new Product("name",new Barcode());
			p.setShelfLife(1);

			Item itm = new Item();
			itm.setProduct(p);

			GregorianCalendar entry = new GregorianCalendar();
			entry.clear();
			entry.set(2011, 0, 28, 0, 0, 0);

			itm.setEntryDate(entry.getTime());

			if (!itm.isExpired())
				result = false;


			System.out.println("Entry Date: " + itm.getEntryDate());
			System.out.println("Exp Date:   " + itm.getExpirationDate());
			System.out.println("Is expired? " + itm.isExpired());

		}
		catch (Exception e)
		{
			System.out.println(e);
			result = false;
		}

		return result;
	}

	public static boolean testSetExitDate()
	{
		boolean result = true;
		Product p=null;
		try		
		{
			p = new Product("name",new Barcode());



			p.setShelfLife(1);

			Item itm = new Item();
			itm.setProduct(p);

			GregorianCalendar entry = new GregorianCalendar();
			entry.clear();
			entry.set(2012, 0, 28, 0, 0, 0);

			GregorianCalendar exit = new GregorianCalendar();
			exit.clear();
			exit.set(2012, 0, 29, 0, 0, 0);

			System.out.println("Entry Date:       " + entry.getTime());
			System.out.println("Exit Date:        " + exit.getTime());

			itm.setEntryDate(entry.getTime());
			itm.setExitTime(exit.getTime());

			System.out.println("Recorded Exit:    " + itm.getExitTime());

		}
		catch (InvalidDataException e) 
		{		
			System.out.println(e);
			result = false;
		}
		


		return result;
	}

	public static boolean testSetEntryDate()
	{
		boolean result = true;

		try		
		{	
			Product p = new Product("name",new Barcode());
			p.setShelfLife(1);

			Item itm = new Item();
			itm.setProduct(p);

			Date today = new Date();
			GregorianCalendar cal = new GregorianCalendar();
			cal.clear();
			cal.set(2012, 0, 28, 0, 0, 0);

			System.out.println("Given Date:       " + cal.getTime());
			itm.setEntryDate(cal.getTime());

			System.out.println("Recorded Date:    " + itm.getEntryDate());
			System.out.println("Expirationd Date: " + itm.getExpirationDate());

		}
		catch (InvalidDataException e) 
		{		
			System.out.println(e);
			result = false;
		} 


		return result;
	}
}
